﻿// =====================================================================
//
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//
// =====================================================================
#region

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using SL.IdentityModel.Protocols.WSTrust;


#endregion

namespace Microsoft.Xrm.Sdk.Client
{
	public abstract class ServiceClient<TService, TClient, TRequest, TEventArgs>
        where TService : class
        where TEventArgs : System.ComponentModel.AsyncCompletedEventArgs
        where TClient : ClientBase<TService>, TService
    {
		private TClient _service;

		private ServiceClient()
		{
		}

		protected ServiceClient(Uri serviceUrl)
		{
			this.ServiceUrl = serviceUrl;

			Initialize();
		}

		protected ServiceClient(IServiceConfiguration<TService> serviceConfiguration)
			: this(serviceConfiguration.ServiceUri)
		{
			ClientExceptionHelper.ThrowIfNull(serviceConfiguration, "serviceConfiguration");

			this.ServiceUrl = serviceConfiguration.ServiceUri;

			this.ServiceConfiguration = serviceConfiguration;

			Initialize();
		}

		protected ServiceClient(IServiceConfiguration<TService> serviceConfiguration,
								RequestSecurityTokenResponse requestSecurityTokenResponse)
		//: this(serviceConfiguration.ServiceUri)
		{
			ClientExceptionHelper.ThrowIfNull(serviceConfiguration, "serviceConfiguration");

			ClientExceptionHelper.ThrowIfNull(requestSecurityTokenResponse, "requestSecurityTokenResponse");

			this.ServiceUrl = serviceConfiguration.ServiceUri;

			this.ServiceConfiguration = serviceConfiguration;

			SecurityTokenReponse = requestSecurityTokenResponse;

			Initialize();
		}


		protected ServiceClient(IServiceManagement<TService> serviceManagement, RequestSecurityTokenResponse securityTokenResponse)
			: this(serviceManagement as IServiceConfiguration<TService>, securityTokenResponse)
		{
			//For now, we'll just convert to the original interface since it already handles the same scenarios.
		}

		public RequestSecurityTokenResponse SecurityTokenReponse { get; set; }

		public Uri ServiceUrl { get; set; }

		public TClient Service
		{
            get { return _service ?? (_service = CreateServiceProxy()); }
			set { _service = value; }
		}

		public IServiceConfiguration<TService> ServiceConfiguration { get; private set; }
		protected CustomBinding ServiceBinding { get; set; }
		protected EndpointAddress ServiceAddress { get; set; }

		protected void Initialize()
		{
			ClientExceptionHelper.ThrowIfNull(ServiceUrl, "ServiceUrl");

			ServiceAddress = new EndpointAddress(ServiceUrl);

			CreateBinding();

			if (ServiceConfiguration == null)
			{
				ServiceConfiguration = ServiceConfigurationFactory.CreateConfiguration<TService>(ServiceUrl,
																								 ServiceConfigurationMetadataLoaded);
			}
		}

		private void ServiceConfigurationMetadataLoaded(object sender, ServiceMetadataLoadedEventArgs e)
		{
			//Nothing to do.
		}

		private void CreateBinding()
		{
			HttpsTransportBindingElement transportBinding = new HttpsTransportBindingElement
																{
																	MaxBufferSize = Int32.MaxValue,
																	MaxReceivedMessageSize = Int32.MaxValue
																};

			ServiceBinding = new CustomBinding(transportBinding);
		}

		protected void SetToken()
		{
			UriBuilder builder = new UriBuilder(ServiceAddress.Uri);
			builder.Path = String.Empty;
			builder.Query = String.Empty;

			IssuedTokenHeader header = new IssuedTokenHeader(SecurityTokenReponse);

			OperationContext.Current.OutgoingMessageHeaders.Add(header);
		}

        private TClient CreateServiceProxy()
        {
            ClientBase<TService> serviceProxy = this.CreateService();
#if !SILVERLIGHT
            serviceProxy.Endpoint.EndpointBehaviors.Add(new ServiceClientBehavior());
#endif
            return (TClient)serviceProxy;
        }

		protected abstract TClient CreateService();

		protected void ServiceClientExecuteCompleted(object sender, TEventArgs e)
		{
			OnExecuteCompleted(e);
		}

		protected void OnExecuteCompleted(TEventArgs args)
		{
			if (ExecuteCompleted != null)
			{
				ExecuteCompleted(this, args);
			}
		}

		public abstract void ExecuteRequest(TRequest request);
		public event EventHandler<TEventArgs> ExecuteCompleted;
	}
}
